package com.navi.heartbeat.repository.impl;

import com.navi.heartbeat.repository.NaviRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.domain.Example;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.List;

@EnableAutoConfiguration
@ComponentScan
public class NaviRepositoryImpl<T , ID extends Serializable>
        extends SimpleJpaRepository<T, ID> implements NaviRepository<T, ID> {

    public static final String DELETE = "DELETE";
    public static final String UPDATE = "UPDATE";
    public static final String INSERT = "INSERT";

    private Logger logger = LoggerFactory.getLogger(NaviRepositoryImpl.class);
    private final EntityManager entityManager;

    public NaviRepositoryImpl(JpaEntityInformation entityInformation,
                              EntityManager entityManager) {
        super(entityInformation, entityManager);

        // Keep the EntityManager around to used from the newly introduced methods.
        this.entityManager = entityManager;
    }

    @Override
    public EntityManager getEntityManager() {
        return entityManager;
    }
    @Override
    public T get(ID id) {
        return super.findOne(id);
    }

    /**
     * 需要加锁的地方，必须在方法前声明事务注解@Transactional ,否则锁将不会成功
     * PS: 之前ICIM版本的库之所以不用@Transactional，是因为在AOP中开启了事务。
     * 这样做的坏处是，有些查询的业务是不需要事务的，全部开启反而降低了系统的效能
     *
     * @param id
     * @return
     */
    @Override
    public T getWithLock(ID id) {
        T t = super.findOne(id);
        entityManager.lock(t, LockModeType.PESSIMISTIC_WRITE);
        return t;
    }

    @Override
    public Query createQuery(String qlString) {
        return entityManager.createQuery(qlString);
    }

    @Override
    public Query createNativeQuery(String sqlString) {
        return entityManager.createNativeQuery(sqlString);
    }

    @Override
    public long count(String countSql) {
        return (Long) entityManager.createQuery(countSql).getSingleResult();
    }

    @Override
    public List<T> find(String hql) {
        return this.createQuery(hql).setMaxResults(200).getResultList();
    }

    @Override
    public List<T> findBySql(String sql) {
        return this.createNativeQuery(sql).getResultList();
    }

    @Override
    public List<T> list(String hql, Object... params) {
        Query query = entityManager.createQuery(hql);
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i + 1, params[i]);
        }
        return query.getResultList();
    }

    public List findBySQL(String sql){
        return entityManager.createNativeQuery(sql).getResultList();
    }

    /**
     * 需要加锁的地方，必须在方法前声明事务注解@Transactional ,否则锁将不会成功
     * PS: 之前icim版本的库之所以不用，是因为在AOP中开启了事务。
     * 这样做的坏处时，有些查询的业务是不需要事务的，全部开启反而降低了系统的效能
     *
     * @param hql
     * @param params
     * @return
     */
    @Override
    public List<T> listWithLock(String hql, Object... params) {
        Query query = entityManager.createQuery(hql);

        for (int i = 0; i < params.length; i++) {
            query.setParameter(i + 1, params[i]);
        }
        final List list = query.getResultList();
        list.forEach(entity -> {
            entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE);
        });
        return list;
    }

    @Override
    public <S extends T> boolean exists(Example<S> example) {
        return super.exists(example);
    }

    @Override
    public <S extends T> S save(S entity) {
        S s = super.save(entity);
        flush();

        return s;
    }

    @Override
    public <S extends T> List<S> save(Iterable<S> entities) {
        List<S> list = super.save(entities);
        flush();

        return list;
    }

    @Override
    public <S extends T> S update(S entity) {
        S s = this.entityManager.merge(entity);
        flush();

        return s;
    }

    @Override
    public <S extends T> List<S> update(List<S> entityList) {
        entityList.forEach(e -> {
            entityManager.merge(e);
        });
        flush();

        return entityList;
    }

    @Override
    public void delete(ID id) {
        final T t = this.get(id);
        delete(t);
        flush();
    }

    @Override
    public void delete(T entity) {
        super.delete(entity);
        flush();
    }

    @Override
    public void delete(Iterable<? extends T> entities) {
        super.delete(entities);
        flush();
    }


    @Override
    public void flush() {
        super.flush();
    }





}